home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 May: Tool Chest / Developer CD Series Tool Chest (Apple Computer)(May 1999).iso / Tool Chest / Games / Game Sample Code / ZAM 1.0a13 / GameSource / Sprite.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-16  |  20.4 KB  |  699 lines  |  [TEXT/KAHL]

  1. #include "CoreAssertion.h"
  2. #include "ZAMProtos.h"
  3. /*
  4.     What is  a sprite?
  5.     In the context of this code a sprite is a small graphic object on the screen that has
  6.     multiple frames, kind of like a movie.  These sprites are loaded from a series of Cicn resources
  7.     which are handy because they have a mask built right into the structure, so it is easier to
  8.     associate the data together.
  9.     
  10.     So, a sprite is like a little movie, you give it a frame rate and a movement rate,
  11.     and this sprite manager will move it automatically for you, depending on flag settings.
  12.     With the flags you can turn off default behavior.  It is also flexible in that it allows you
  13.     to install callback routines for each animation frame drawn, and each time a sprite is moved.
  14.     Also, each sprite has a colission handler, and when you request that colissions be checked for
  15.     your sprite colission handler will be called when it overlaps another sprite.
  16.     
  17.     This is all done with built in features of the Macintosh and QuickDraw.  Thanks Bill and Kon.
  18.     
  19.     Some of this code is hairy and messy.  If I were starting this over again today, knowing what
  20.     I do now having done it once, I would do it differently.  Hopefully, since I am making this
  21.     code available to you, you will see what I mean and be able to implement your own animation
  22.     architecture that fits your needs and works for you.
  23.     
  24.     CoMation™ Architecture by Brigham Stevens
  25.     
  26.     Be sure to put CoMation on the box of your game or MultiMedia application if you
  27.     are sychronizing interactive animation between multiple computers.  Thats right.
  28.     
  29.     You can say  CoMation Architecture Support right on your box, and the customers will
  30.     clamor for more.
  31.     
  32.     
  33.     Hey for more excellent examples of Macintosh animation, see SpriteWorld by Tony Myles.  I
  34.     think it is on the developer CD, perhaps in the same area as this one.
  35. */
  36.  
  37. /* this is where all the sprites are kept */
  38. /* applications should keep their own references to the sprites */
  39. /* see TankSprites, MissileSprites, and ExplosionSprites for examples of using */
  40. /* most of the routines here */
  41.  
  42. static spriteLayerPtr    MasterSpriteHead;
  43. static spriteLayerPtr    MasterSpriteTail;
  44.  
  45. void AnimateSprites(void)
  46. /*
  47.     This function is what you call once each time through your main loop.
  48.     It will erase and draw all the sprites that are flagged for update.
  49.     This handles the layering by doing all the necessary erasing first.
  50.     A sprite is erased by CopyBitsing the background in over its previous location.
  51.     
  52.     Then the sprites are drawn in layer order.  NOTE that this does NOT happen unless
  53.     at least one sprite was erased.  This may be a flaw, but it saves time.
  54.     
  55.     The sprites are drawn using a mask region, which has to be moved to the current position
  56.     each time.  That is what MoveCellMaskRgnToRect.   A Cell is an animation cell, or frame.
  57.     Each frame in a sprite frame set has its own mask region.
  58.     
  59.     There are a lot of flags used by this, so be careful.  Sometimes the flags gave me grief, but
  60.     I wanted to make it flexible.  Also, lots of the flags were added in for the AppleEvent
  61.     support.
  62.     
  63.     Remember, if I was going to do this again, it would be done a lot differently.  But, this
  64.     works pretty well as it is.
  65. */
  66. {
  67.     register spritePtr            spr;
  68.     register spriteLayerPtr        sprLayer;
  69.     register frameCellPtr        curFrame;
  70.  
  71.     register PixMapHandle        srcPix;
  72.     register PixMapHandle        destPix;
  73.  
  74.     register Boolean            needToDraw = false;
  75.     
  76.     /* Erase all sprites that need to be erased */
  77.     for(sprLayer = MasterSpriteTail; sprLayer != nil; sprLayer = sprLayer->prev) {
  78.         if( (sprLayer->layerFlags & kLayerDirty) != 0) {
  79.             needToDraw = true;
  80.             for(spr = sprLayer->sprites; spr != nil; spr = spr->next) {
  81.                 if( (spr->spriteFlags & kNeedsToBeErased) != 0) {
  82.                     curFrame = spr->frameList->finfo.curImage;
  83.  
  84.                     srcPix = PreserveGraf(sprLayer->backdrop);
  85.                     destPix = PreserveGraf(sprLayer->tween);
  86.                 
  87.                     //  ERASE from backdrop to the tween layer
  88.                     
  89.                     CopyBits(*srcPix,
  90.                              *destPix,
  91.                              &spr->prevBounds,
  92.                              &spr->prevBounds,
  93.                              srcCopy,
  94.                              nil);
  95.                     
  96.                     RestoreGraf();
  97.                     RestoreGraf();
  98.                     
  99.                     spr->spriteFlags &= ~kNeedsToBeErased;
  100.                 }
  101.             }
  102.         }
  103.     }
  104.             
  105.     /* draw all sprites in layer order */
  106.     if( needToDraw ) {
  107.         for(sprLayer = MasterSpriteTail; sprLayer != nil; sprLayer = sprLayer->prev) {
  108.             for(spr = sprLayer->sprites; spr != nil; spr = spr->next) {
  109.                 if(spr->visible) {
  110.                     curFrame = spr->frameList->finfo.curImage;
  111.                             
  112.                     srcPix = PreserveGraf(curFrame->image);
  113.                     destPix = PreserveGraf(sprLayer->tween);
  114.     
  115.                     MoveCellMaskRgnToRect(curFrame, &spr->bounds);
  116.                 
  117.                     //  DRAW the sprite into the offscreen tween layer
  118.                     
  119.                     CopyBits(*srcPix,
  120.                              *destPix,
  121.                              &curFrame->image->portRect,
  122.                              &spr->bounds,
  123.                              srcCopy,
  124.                              curFrame->mask);
  125.                     
  126.                     RestoreGraf();
  127.                     RestoreGraf();
  128.                     
  129.                     spr->prevBounds = spr->bounds;
  130.                 }
  131.             }
  132.         }
  133.     }
  134.     
  135.     /* draw all sprites that need to be updated on the screen */
  136.     if( needToDraw ) {
  137.         for(sprLayer = MasterSpriteTail; sprLayer != nil; sprLayer = sprLayer->prev) {
  138.             if( (sprLayer->layerFlags & kLayerDirty) != 0) {
  139.                 for(spr = sprLayer->sprites; spr != nil; spr = spr->next) {
  140.                     if(spr->spriteFlags & kNeedsToBeDrawn) {
  141.                         srcPix = PreserveGraf(sprLayer->tween);
  142.                         destPix = PreserveGraf((GWorldPtr)sprLayer->window);
  143.         
  144.                         //  DRAW the sprite to the window on screen
  145.                         
  146.                         CopyBits(*srcPix,
  147.                                  *destPix,
  148.                                  &spr->updateBounds,
  149.                                  &spr->updateBounds,
  150.                                  srcCopy,
  151.                                  nil);
  152.                         
  153.                         RestoreGraf();
  154.                         RestoreGraf();
  155.                         
  156.                         spr->spriteFlags &= ~kNeedsToBeDrawn;
  157.                     }
  158.                 }
  159.                 sprLayer->layerFlags &=  ~kLayerDirty;
  160.             }
  161.         }
  162.     }
  163. }
  164.  
  165. void SpriteUpdateEvent(void)
  166. /*
  167.     Call this from your event loop when a window containing sprites
  168.     has an update event.  This just refreshes the screen from the
  169.     offscreen sprite world, which should be current.
  170. */
  171. {
  172.     
  173.     PixMapHandle    srcPix;
  174.     PixMapHandle    destPix;
  175.     
  176.     
  177.     if(MasterSpriteHead) {
  178.         srcPix = PreserveGraf(MasterSpriteHead->tween);
  179.         destPix = PreserveGraf((GWorldPtr)MasterSpriteHead->window);
  180.         CopyBits(*srcPix,
  181.                  *destPix,
  182.                  &MasterSpriteHead->tween->portRect,
  183.                  &MasterSpriteHead->tween->portRect,
  184.                  srcCopy,
  185.                  nil);        
  186.         RestoreGraf();
  187.         RestoreGraf();
  188.     }
  189. }
  190.  
  191. void InitSprites(void)
  192. /*
  193.     Call this routine very early in the program.
  194.     It pre-allocates memory for all the sprite records as non-relocatable blocks.
  195.     As you can see, it does not pre-allocate anything, which may slow things down
  196.     if you are dynamically allocating memory at runtime.  I changed this to simplify the
  197.     use of Sprites, but my program pre-allocates all the sprites anyway.
  198. */
  199. {
  200.  
  201.     MasterSpriteHead = nil;
  202.     MasterSpriteTail = nil;
  203.     
  204. }
  205.  
  206. OSErr    CreateSpriteLayer(spriteLayerPtr *retSprite, 
  207.                             GWorldPtr tween, 
  208.                             GWorldPtr backdrop, 
  209.                             WindowPtr spriteWin)
  210. /*
  211.     Sprites live in layers that define which sprites overlap each other.
  212.     Layers also make it easy to group sprites for colission detection.
  213.     Thanks Tony for the layering concept.  My first cut through this
  214.     was not using layers, just one big gantic sprite list.  Yep, gantic.
  215. */
  216. {
  217.  
  218.     OSErr            err = noErr;
  219.     spriteLayerPtr    sl;
  220.     
  221.     sl = (spriteLayerPtr)NewPtrClear(sizeof(spriteLayer));
  222.     if(!sl) {
  223.         err = MemError();
  224.         ErrMsgCode("\pCreateSpriteLayer NewPtrClear failed.",err);
  225.     }
  226.     
  227.     if(err == noErr) {
  228.         sl->tween = tween;
  229.         sl->backdrop = backdrop;
  230.         sl->window = spriteWin;
  231.     }
  232.  
  233.     /* add this layer to the layer list */
  234.     /* layers are created in front to back order */
  235.     if(MasterSpriteTail) {
  236.         sl->prev = MasterSpriteTail;
  237.         MasterSpriteTail->next = sl;
  238.         MasterSpriteTail = sl;
  239.     } else {
  240.         MasterSpriteHead = MasterSpriteTail = sl;
  241.     }
  242.  
  243.     *retSprite = sl;
  244.     
  245.     return err;
  246. }
  247.  
  248.  
  249. void StopSpriteAction(spritePtr spr)
  250. /*
  251.     This removes the sprites from the time manager queue
  252.     if they are currently active.
  253.     The Time Manager seems like it removes tasks when they complete
  254.     because it has been changed to only insert them when they are primed,
  255.     so, this will only remove the task if the bit is set indicating that it is
  256.     active.  PrimeTime sets this bit.
  257. */
  258. {
  259.     if( (spr->frameTask.timer.qType & TaskActiveFlag) != 0) {
  260.         (void)RmvTime(&spr->frameTask.timer);
  261.     }
  262.     if( (spr->moveTask.timer.qType & TaskActiveFlag) != 0) {
  263.         (void)RmvTime(&spr->moveTask.timer);
  264.     }
  265.  
  266. }
  267.  
  268. void StopSpriteLayerAction(spriteLayerPtr sprLayer)
  269. /*
  270.     Freeze an entire sprite layer.
  271.     This does not deallocate the sprite at all.
  272.     
  273.     Only stops it from moving around so much.
  274. */
  275. {
  276.     spritePtr    killSprite;
  277.     
  278.     for(killSprite = sprLayer->sprites; killSprite != nil; killSprite = killSprite->prev)
  279.         StopSpriteAction(killSprite);
  280. }
  281.  
  282. void KillSprites(void)
  283. /*
  284.     Perhaps a misleading name, 
  285.     because this does not deallocate either.  This just stops all Layers at once.
  286.     I guess I never wrote a sprite deallocator, because it is complicated because
  287.     frame sets are shared.  I should add a user count to the frame set so that they
  288.     know when no one else is using it, then the deallocator could be written.
  289.     
  290.     However, since ZAM only loads sprites once, I don't need one, so I'm not writing it.
  291. */
  292. {
  293.     spriteLayerPtr    killLayer;
  294.     
  295.     for(killLayer = MasterSpriteTail; killLayer != nil; killLayer = killLayer->prev)
  296.         StopSpriteLayerAction(killLayer);
  297. }
  298.  
  299.  
  300. void AddSpriteToLayer(spritePtr spr, spriteLayerPtr sprLayer)
  301. /*
  302.     Yes, this will take the sprite and make it a member of the layer.
  303. */
  304. {
  305.  
  306.     spr->next = sprLayer->sprites;
  307.     sprLayer->sprites->prev = spr->next;
  308.     sprLayer->sprites = spr;
  309. }
  310.  
  311. void RemoveSpriteFromLayer(spritePtr spr, spriteLayerPtr sprLayer)
  312. {
  313.     if(spr->next) {
  314.         spr->next->prev = spr->prev;
  315.     }
  316.     
  317.     if(spr->prev) {
  318.         spr->prev->next = spr->next;
  319.     }
  320.     
  321.     spr->prev = nil;
  322.     spr->next = nil;
  323.     
  324. }
  325.  
  326. void MoveCellMaskRgnToRect(frameCellPtr curFrame, Rect *r)
  327. /*
  328.     Offset a region to move it with the sprite.
  329.     The maskLoc is the original topLeft of the region, and
  330.     it is needed to preserve the region position within the sprite rectangle
  331. */
  332. {
  333.     Point                rgnOffset;
  334.     
  335.     /* translate the region position to the new image position */
  336.     rgnOffset.h = r->left - (**curFrame->mask).rgnBBox.left 
  337.                 + curFrame->maskLoc.h;
  338.     rgnOffset.v = r->top - (**curFrame->mask).rgnBBox.top 
  339.                 + curFrame->maskLoc.v;
  340.     OffsetRgn(curFrame->mask, rgnOffset.h, rgnOffset.v);
  341. }
  342.  
  343.  
  344. OSErr CreateEmptySprite(spriteLayerPtr    sprLayer,
  345.                         spritePtr  *newSprite,         /* new sprite returned here */
  346.                         long        spriteFlags,
  347.                         long        moveTimeInterval,
  348.                         long        frameTimeInterval,
  349.                         long        refCon)        /* application value */
  350. /*
  351.     Create a new sprite from parameters specified with no frame set.
  352.     To add a frame set, either use CreateEmptyFrameSet, and then SetSpriteFrameSet
  353.     to copy a frame set to it.
  354.     
  355.     Some things about this are bad.  FrameSet headers are block moved around
  356.     when the frame sets are shared.  This is not good, but it is not that much memory.
  357.     See SpriteFrameSet.c for more info on frame sets.
  358. */
  359. {
  360.     spritePtr    tSprite;
  361.     OSErr        err = noErr;
  362.     
  363.     tSprite = (spritePtr)NewPtrClear(sizeof(sprite));
  364.  
  365.     if(tSprite == nil) {
  366.         err = paramErr;
  367.         ErrMsg("\pNo More Sprites may be allocated. ••••EXITING••••.");
  368.     } else {
  369.         tSprite->usrNext = nil;
  370.         tSprite->usrPrev = nil;
  371.         tSprite->moveHandler = nil;
  372.         tSprite->visible = false;
  373.         tSprite->loc.h = 0;
  374.         tSprite->loc.v = 0;
  375.         tSprite->vel.h = 0;
  376.         tSprite->vel.v = 0;
  377.         tSprite->frameList = nil;
  378.         tSprite->refCon = refCon;
  379.         tSprite->spriteFlags = spriteFlags;
  380.         tSprite->inUse = false;
  381.         tSprite->moveTimeInterval = moveTimeInterval;
  382.         tSprite->frameTimeInterval = frameTimeInterval;
  383.         tSprite->ownerLayer = sprLayer;        
  384.         *newSprite = tSprite;
  385.         AddSpriteToLayer(tSprite, sprLayer);
  386.  
  387.     }
  388.         
  389.     return err;
  390. }
  391.  
  392. OSErr CreateColorIconSprite(spriteLayerPtr    sprLayer,
  393.                             spritePtr  *newSprite,     /* new sprite returned here */
  394.                             short        startID,     /* starting resource ID of cicn */
  395.                             short        numFrames,    /* number of resources to load */
  396.                             long        spriteFlags,
  397.                             long        moveTimeInterval,
  398.                             long        frameTimeInterval,
  399.                             long        refCon)        /* application value */
  400.  
  401. /*
  402.     Create a new sprite from parameters specified
  403.     starting location for all sprites is 0,0
  404.     velocity is 0,0
  405.     bounds taken from icon dimensions - GWORLD of first frame
  406.     assumes that all icons have the same dimension
  407.     center - calcd from bounds
  408.     dimension - h width v = height calcd from bounds
  409.     frameList - built from color icons starting from startID
  410.     visible - set to false
  411.     
  412.     Sprites created with this can then be copied.  See MissileSprites.c (LoadMIssileSprites)
  413.     for an example, or ExplosionSprites.c.
  414. */
  415. {
  416.     spritePtr    tSprite;
  417.     OSErr        err;
  418.     
  419.     /*  create an empty sprite first */
  420.     err = CreateEmptySprite(sprLayer,
  421.                             &tSprite,     /* new sprite returned here */
  422.                             spriteFlags,
  423.                             moveTimeInterval,
  424.                             frameTimeInterval,
  425.                             refCon);    /* application value */
  426.     
  427.     /* now create the frameset list, and attach it to the sprite */
  428.         
  429.     if(err == noErr) {
  430.         err = CreateColorIconFrameSet(&tSprite->frameList, startID,  numFrames);
  431.         if(err != noErr) {
  432.                 ErrMsgCode("\pError in CreateColorIconFrameSet!",err);
  433.         } else {
  434.             *newSprite = tSprite;
  435.         }
  436.     }
  437.     
  438.     return err;
  439. }
  440.  
  441. void SetSpriteLoc(spritePtr spr, Fixed h, Fixed v)
  442. /*
  443.     Change the position of the sprite on the screen
  444. */
  445. {
  446.     Boolean showIt = false;
  447.     
  448.     if(spr->visible) {
  449.         showIt = true;
  450.         HideSprite(spr);
  451.     }
  452.     
  453.     spr->prevBounds = spr->bounds;
  454.  
  455.     spr->loc.h = h;
  456.     spr->loc.v = v;
  457.     
  458.     spr->bounds.top =  FixToInt(spr->loc.v) - spr->frameList->finfo.center.v;
  459.     spr->bounds.left = FixToInt(spr->loc.h) - spr->frameList->finfo.center.v;                
  460.     spr->bounds.bottom = spr->bounds.top + spr->frameList->finfo.dimension.v;
  461.     spr->bounds.right = spr->bounds.left + spr->frameList->finfo.dimension.h;
  462.     
  463.     MyUnionRect(&spr->prevBounds, &spr->bounds, &spr->updateBounds);
  464.     spr->spriteFlags |= kNeedsToBeErased | kNeedsToBeDrawn;
  465.  
  466.     if(showIt)
  467.         ShowSprite(spr);
  468.  
  469.  
  470. void ShowSprite(spritePtr spr)
  471. /*
  472.     Make the sprite visibile.
  473. */
  474. {
  475.     if(!spr->visible) {
  476.         spr->visible = true;
  477.         spr->spriteFlags |= kNeedsToBeDrawn;
  478.     }
  479. }
  480.  
  481. void HideSprite(spritePtr spr)
  482. /*
  483.     hide the sprite on the screen
  484.     and draw it.
  485.     In this case if the sprite was not ever
  486.     previously drawn, the drawing routine will
  487.     not draw anything.
  488. */
  489. {
  490.     if(spr->visible) {
  491.         spr->visible = false;
  492.         spr->spriteFlags |= kNeedsToBeErased;
  493.     }
  494. }
  495.  
  496.  
  497. void StartSpriteAction(spritePtr spr)
  498. /*
  499.     This launches the XThing tasks for updating the sprites, which in turn
  500.     uses the Time Manger.  XThings are periodical tasks that run as close
  501.     if any of the intervals are zero, then the task is not launched 
  502. */
  503. {
  504.     if(spr->frameTimeInterval) {
  505.         (void)StartXThing(&spr->frameTask, spr->frameTimeInterval, 
  506.                 (updateProc)SpriteFrameTask, (long)spr);
  507.     }
  508.     
  509.     if(spr->moveTimeInterval) {
  510.         (void)StartXThing(&spr->moveTask, spr->moveTimeInterval, 
  511.                 (updateProc)SpriteMoveTask, (long)spr);
  512.     }
  513. }
  514.  
  515. void StartRemoteSpriteAction(spritePtr spr)
  516. /*
  517.     Same as above, only the timer is never fired.
  518.     Instead, they are manually updated when a network message is
  519.     received saying to update the sprite.
  520. */
  521. {
  522.         (void)AddXThing(&spr->frameTask, spr->frameTimeInterval, 
  523.                 (updateProc)SpriteFrameTask, (long)spr);
  524.     
  525.         (void)AddXThing(&spr->moveTask, spr->moveTimeInterval, 
  526.                 (updateProc)SpriteMoveTask, (long)spr);
  527. }
  528.  
  529.  
  530. Boolean SpriteFrameTask(xthing *xtp, spritePtr spr)
  531. /*
  532.     This is the XThing task that changes the frame of the sprite.
  533.     
  534.     If the kFrameTaskBeforeUpdate bit it set in spriteFlags and if there
  535.     is a global frame task installed, then it will be called before
  536.     the frame of the sprite is changed.
  537.     
  538.     If the kDefaultFrameAdvance bit is set, then the task
  539.     will go ahead and advance the frame on to the next one.
  540.     
  541.     if the kFrameTaskAfterUpdate bit is set, then the task will
  542.     also call the global frame task after the frame is advanced.
  543.     
  544.     Finally, if the current frame has a callback set up, then it will be called.
  545.     
  546.     All of the callbacks return a boolean that determines if this task is to be rescheduled.
  547. */
  548. {
  549.     register frameCellPtr    curFrame;
  550.     register frameSetPtr        frameset;
  551.     register long            frameDelay;
  552.     register Boolean            reTimeTask = true;
  553.     
  554.     frameset = spr->frameList;
  555.  
  556.     if( (spr->spriteFlags & kFrameTaskBeforeUpdate) != 0) {
  557.         if(spr->frameHandler) {
  558.             reTimeTask = (*spr->frameHandler)(spr, nil);
  559.         }
  560.     }
  561.     
  562.     if( (spr->spriteFlags & kDefaultFrameAdvance) != 0) {
  563.         frameset->finfo.frameIndex++;
  564.     
  565.         if(frameset->finfo.frameIndex >= frameset->finfo.frameCount) {
  566.             frameset->finfo.frameIndex    = 0;
  567.         }
  568.         
  569.         curFrame = &frameset->flist[frameset->finfo.frameIndex];
  570.         frameset->finfo.prevImage = frameset->finfo.curImage;
  571.         frameset->finfo.curImage = curFrame;
  572.         spr->updateBounds = spr->bounds;
  573.         spr->spriteFlags |= kNeedsToBeDrawn | kNeedsToBeErased;
  574.         spr->ownerLayer->layerFlags |= kLayerDirty;
  575.     }
  576.     
  577.     if( (spr->spriteFlags & kFrameTaskAfterUpdate) != 0) {
  578.         if(spr->frameHandler) {
  579.             reTimeTask = (*spr->frameHandler)(spr, (struct frameCell *)-1);
  580.         }
  581.     }
  582.  
  583.     /* check if this frame has a callback and call it */
  584.     curFrame = &frameset->flist[frameset->finfo.frameIndex];
  585.     if(curFrame->frameCB) {
  586.         reTimeTask = (*curFrame->frameCB)(spr,  (struct frameCell *)curFrame);
  587.     }
  588.     
  589.     if( (spr->spriteFlags & kRemoteSprite) != 0)
  590.         reTimeTask = false;
  591.     
  592.     return reTimeTask;
  593.     
  594. }
  595.  
  596.  
  597. Boolean SpriteMoveTask(xthing *xtp, spritePtr spr)
  598. /*
  599.     This procedure moves the sprites in the default way,
  600.     applying the velocity to the location, and then recalculating the
  601.     bounds based on this position.
  602.     
  603.     This procedure changes fields that DrawSprite depends upon:
  604.     
  605.     updateBounds covers the total area of the screen that needs to be changed,
  606.     incorporating the previous position and the current position.
  607.     
  608.     prevBounds is the position the sprite WAS in.  This is used for erasing the
  609.     previous image.
  610.     
  611.     prevImage is the previous frameCell the sprite was drawn with.  This is used to
  612.     erase the previous image.
  613.     
  614. */
  615. {
  616.  
  617.     moveProc    moveJSR;
  618.     Boolean        result = true;
  619.     short        adjustPos;
  620.     Boolean        reAdjustNecessary;
  621.     
  622.     if( (spr->vel.h != 0) || (spr->vel.v != 0) ) {
  623.         spr->prevBounds = spr->bounds;    /* save previous location */
  624.             
  625.         /* the sprite location is the center of the sprite */    
  626.         if(spr->spriteFlags & kRemoteUpdate) {
  627.             spr->loc = spr->remoteLoc;        /* slam the sprite */
  628.         } else {
  629.             spr->loc.h += spr->vel.h;        /* offset the sprite */
  630.             spr->loc.v += spr->vel.v;        
  631.         }
  632.         
  633.         /* build the sprite integer rectangle location from the fixed center point */
  634.         spr->bounds.top =  FixToInt(spr->loc.v) - spr->frameList->finfo.center.v;
  635.         spr->bounds.left = FixToInt(spr->loc.h) - spr->frameList->finfo.center.v;                
  636.         spr->bounds.bottom = spr->bounds.top + spr->frameList->finfo.dimension.v;
  637.         spr->bounds.right = spr->bounds.left + spr->frameList->finfo.dimension.h;
  638.     
  639.         /* calculate the entire area that needs updating */
  640.         MyUnionRect(&spr->bounds, &spr->prevBounds, &spr->updateBounds);
  641.     
  642.         spr->frameList->finfo.prevImage = spr->frameList->finfo.curImage;
  643.         
  644.         /* set the update flag if we have moved a whole pixel at least */
  645.         if(spr->prevBounds.top != spr->bounds.top) {
  646.             spr->spriteFlags |= kNeedsToBeDrawn | kNeedsToBeErased;
  647.             spr->ownerLayer->layerFlags |= kLayerDirty;
  648.         }
  649.         else if(spr->prevBounds.left != spr->bounds.left) {
  650.             spr->spriteFlags |= kNeedsToBeDrawn | kNeedsToBeErased;
  651.             spr->ownerLayer->layerFlags |= kLayerDirty;
  652.         }
  653.     
  654.         /* this constrains the sprite to within the constrain rectangle */
  655.         if(spr->spriteFlags & kConstrainToRect) {
  656.             reAdjustNecessary = false;
  657.             if(spr->bounds.right > spr->constrainRect.right) {
  658.                 adjustPos = spr->bounds.right - spr->constrainRect.right;
  659.                 spr->loc.h -= ff(adjustPos);
  660.                 reAdjustNecessary = true;
  661.             } else if(spr->bounds.left < spr->constrainRect.left) {
  662.                 adjustPos =   spr->constrainRect.left - spr->bounds.left;
  663.                 spr->loc.h += ff(adjustPos);
  664.                 reAdjustNecessary = true;
  665.             }
  666.             
  667.             if(spr->bounds.bottom > spr->constrainRect.bottom) {
  668.                 adjustPos = spr->bounds.bottom - spr->constrainRect.bottom;
  669.                 spr->loc.v -= ff(adjustPos);
  670.                 reAdjustNecessary = true;
  671.             } else if(spr->bounds.top < spr->constrainRect.top) {
  672.                 adjustPos = spr->constrainRect.top - spr->bounds.top;
  673.                 spr->loc.v += ff(adjustPos);
  674.                 reAdjustNecessary = true;
  675.             }
  676.             if(reAdjustNecessary) {
  677.                 spr->bounds.top =  FixToInt(spr->loc.v) - spr->frameList->finfo.center.v;
  678.                 spr->bounds.left = FixToInt(spr->loc.h) - spr->frameList->finfo.center.v;                
  679.                 spr->bounds.bottom = spr->bounds.top + spr->frameList->finfo.dimension.v;
  680.                 spr->bounds.right = spr->bounds.left + spr->frameList->finfo.dimension.h;
  681.             }
  682.         }
  683.  
  684.     }
  685.         
  686.     /* check if there is a move callback */
  687.     if(spr->moveHandler) {
  688.         moveJSR = (moveProc)spr->moveHandler;
  689.         result = (*moveJSR)(spr);
  690.     } 
  691.     
  692.     /* remote sprites are not timed by the time manager.  They are moved when an update is rcvd */
  693.     if( (spr->spriteFlags & kRemoteSprite) != 0)
  694.         result = false;
  695.  
  696.     /* tell XThing if we want to be added back as time manager task */
  697.     return result;
  698. }